#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "cJSON.h"
#include "zq_gl.h"

// 读取UTC1970年以来的秒 本地时间(加时区)
unsigned int ZqGetLocalStample(void)
{
	ql_rtc_time_t tm;
    // 取local时间
    ql_rtc_get_localtime(&tm);
    // 计算1970年以来的秒
    return ZqDateToTimestamp((uint16_t)tm.tm_year, (uint16_t)tm.tm_mon, (uint16_t)tm.tm_mday, (uint16_t)tm.tm_hour, (uint16_t)tm.tm_min, (uint16_t)tm.tm_sec);
}
unsigned int ZqGetUTCStample(void)
{
    ql_rtc_time_t tm;
    // 取utc时间
    ql_rtc_get_time(&tm);
    // 计算1970年以来的秒
    return ZqDateToTimestamp((uint16_t)tm.tm_year, (uint16_t)tm.tm_mon, (uint16_t)tm.tm_mday, (uint16_t)tm.tm_hour, (uint16_t)tm.tm_min, (uint16_t)tm.tm_sec);
}
void ZqSetUTCStample(unsigned int sec)
{
    time_t t = sec;
    struct tm *ptm = gmtime(&t);
    ql_rtc_time_t qtm;
    qtm.tm_year = ptm->tm_year - 100 + 2000;
    qtm.tm_mon = ptm->tm_mon + 1;
    qtm.tm_mday = ptm->tm_mday;
    qtm.tm_hour = ptm->tm_hour;
    qtm.tm_min = ptm->tm_min;
    qtm.tm_sec = ptm->tm_sec;
    qtm.tm_wday = ptm->tm_wday;
    // 取utc时间
    ql_rtc_set_time(&qtm);
}
// get yyyyMMddHHmmss 格式, 带时区本地local时间
void ZqGetRTC_yyyyMMddHHmmss(char* buf)
{
    ql_rtc_time_t tm;
    // get local time 
    ql_rtc_get_localtime(&tm);  
    sprintf(buf, "%04d%02d%02d%02d%02d%02d", tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
// translate the data[] to hex string sizeof(pout) must >= sizeof(pin)*3+1
void zq_HexToAscii(unsigned char *pin, int inlen, char *pout)
{
	// char sigalbuf[5] = {0};
	for(int i = 0; i < inlen; i++)
		sprintf(pout+(2*i), "%02X ", pin[i]);
	return;
}

// is file exist 0 - exist; -1 - not exist
int zq_EasyIsFileExisted(const char *path)
{
	QFILE fp = ql_fopen(path, "r");
	if((long long)fp <= 0) return 0;
    ql_fclose(fp);
    return 1;
}
// read all file content in buf
int zq_EasyFileRead(char *path, void *buf, int size)
{
    //char* pp = path;
	QFILE fp = ql_fopen(path, "rb");
	if((long long)fp <= 0)
    {
        ZQLOG("@err@: Open file error! path = %s", path);
    	return ZQ_RET_FILE_ERR;
    }
	//int fsize = ql_fsize(fp);
 //   if(fsize < 0 || fsize > size) // assert file size in this module must less than 10k
 //   {
 //       ZQ_LOG_FAILED(ql_fsize, size);
 //   	return ZQ_RET_FILE_ERR;
 //   }
    // *buf = malloc(size+1);      // just to be sure, add \0 at the end of the buf
    // if(*buf == NULL) 
    // {
    //     ql_fclose(fp);
    //     ZQ_LOG_FAILED(malloc, 0);
    //     return ZQ_RET_MEM_ERR;
    // }
    int fsize = ql_fread(buf, 1, size, fp);
    // (*buf)[size] = 0;
    ql_fclose(fp);
    return fsize;
}
// write all buffer content to a file
int zq_EasyFileWrite(char *path, void *buf, int size)
{
	QFILE fp = ql_fopen(path, "wb");
	if((long long)fp <= 0)
    {
        ZQLOG("@err@: Open file error! path = %s", path);
    	return ZQ_RET_ERR;
    }
	int n = ql_fwrite(buf, 1, size, fp);
    ql_fclose(fp);
    if (n != size)
    {
        ZQ_LOG_FAILED(ql_fwrite, n);
    	return ZQ_RET_ERR;
    }
    return size;
}
// mkdir
int zq_EasyMkDir(const char *path)
{
    int ret = ql_mkdir(path, 0);
    if(ret < 0 && ret != QL_DIR_DIR_ALREADY_EXIST)
    {
        ZQ_LOG_FAILED(ql_mkdir, ret);
    	return ZQ_RET_ERR;
    }
    else
        return ZQ_RET_OK;
}
#define NET_REG_MAX_CNT             	(14)
bool ZqRegisterNetwork(void)
{
    int ret = 0;
    uint8_t cfuntmp = 0, retry = 0;
    ql_data_call_default_pdn_cfg_s apn_cfg_def = { 0 };
    ql_nw_reg_status_info_s nw_info = { 0 };

    for( int i= 0; i < 3; ql_rtos_task_sleep_s(5)) { // retry 3 times
        ZQLOG("ZqRegisterNetwork, times = %d", i);
        if (ql_datacall_get_sim_profile_is_active(ZQ_SIM_0, ZQ_PROFILE_IDX) == 1) 
            return true;
        ZQLOG("ql_nw_get_reg_status");
        ql_nw_get_reg_status(ZQ_SIM_0, &nw_info);
        if ((QL_NW_REG_STATE_HOME_NETWORK == nw_info.data_reg.state) || (QL_NW_REG_STATE_ROAMING == nw_info.data_reg.state))
            return true;
        if(ZQ_APN_ABROAD != 1) continue; // 不是外国平台，不需要设置APN。
        // 重新配置APN。
        ZQLOG("ql_dev_get_modem_fun");
        ret = ql_dev_get_modem_fun(&cfuntmp, ZQ_SIM_0);
        if(ret != 0) continue;
        if (cfuntmp != QL_DEV_CFUN_MIN) {
            ZQLOG("ql_dev_set_modem_fun: %d", QL_DEV_CFUN_MIN);
            ret = ql_dev_set_modem_fun(QL_DEV_CFUN_MIN, 0, ZQ_SIM_0);
            if (ret != 0) continue;
        }
        strcat(apn_cfg_def.apn_name, ZQ_APN_NAME);
        strcat(apn_cfg_def.username, "");
        strcat(apn_cfg_def.password, "");
        apn_cfg_def.ip_version = QL_PDP_TYPE_IP;
        ZQLOG("ql_datacall_set_default_pdn_cfg, APN=%s", apn_cfg_def.apn_name);
        ret = ql_datacall_set_default_pdn_cfg(ZQ_SIM_0, &apn_cfg_def);
        if (ret != 0) continue;

        ZQLOG("ql_dev_set_modem_fun: %d", QL_DEV_CFUN_MIN);
        ret = ql_dev_set_modem_fun(QL_DEV_CFUN_FULL, 0, ZQ_SIM_0);

        ql_rtos_task_sleep_s(2);

        for(; retry < NET_REG_MAX_CNT; retry++, ql_rtos_task_sleep_ms(5000)){
            ZQLOG("ql_nw_get_reg_status, try  times: %d", retry+1);
            ql_nw_get_reg_status(ZQ_SIM_0, &nw_info);
            if ((QL_NW_REG_STATE_HOME_NETWORK == nw_info.data_reg.state) || (QL_NW_REG_STATE_ROAMING == nw_info.data_reg.state)) {
                ZQLOG("register success, state = %d", nw_info.data_reg.state);
                return true;
            }
        }
    }
    ZQLOG("ZqRegisterNetwork, last error = %d", ret);
    return false;
}
// start a data call
int zq_StartDataCall(void)
{
    ql_data_call_info_s info;
    char ip4_addr_str[16] = { 0 };
    // zq_glvar.net_info.isgetip = DEV_GETIP_NG;

    // check if pdp actived, data call is completed
    if (ql_datacall_get_sim_profile_is_active(ZQ_SIM_0, ZQ_PROFILE_IDX) == 1)
    {
        ZQLOG("data call has completed before !!!");
        return ZQ_RET_OK;
    }
    ZQLOG("wait for data call complete");
    for(int i = 0; i < 10; i++, ql_rtos_task_sleep_s(1)) {
        // kg modify 20231030, 把这个函数放到程序的开头，只要上电等待一次，其他时候断网重拨号，为了拨号速度，刻意不调这个函数，反正系统自动注册
        // wait register ready, system will auto register to base station, 
        // if (ql_network_register_wait(ZQ_SIM_0, 120) != 0) continue;
        // wait register ready, system will auto register to base station, 
        if (!ZqRegisterNetwork()) {
            ZQLOG("@err@: register network failed!");
            return ZQ_RET_ERR;
        }
        // set sync mode
        ql_set_data_call_asyn_mode(ZQ_SIM_0, ZQ_PROFILE_IDX, 0);
        // start data call
        if (ql_start_data_call(ZQ_SIM_0, ZQ_PROFILE_IDX, QL_PDP_TYPE_IP, ZQ_APN_NAME, NULL, NULL, 0) != 0) continue;
        // get data call info
        if (ql_get_data_call_info(ZQ_SIM_0, ZQ_PROFILE_IDX, &info) != 0) continue;
        inet_ntop(AF_INET, &info.v4.addr.ip, ip4_addr_str, sizeof(ip4_addr_str));
        ZQLOG("data call completed, get IPv4 address: %s", ip4_addr_str);
        return ZQ_RET_OK;
    }
    ZQLOG("@err@：data call failed.");
    return ZQ_RET_ERR;
}

// 同步RTC时钟，利用拨号和基站同步
// 注意：1. 函数会断网，重新拨号。本地时钟明显异常(year < 2023)会有意阻塞一直到校准完成。
//void ZySyncRTC(void)
//{
//    // 配置拨号基站同步时间模式
//    ql_rtc_cfg_t cfg;
//    ql_rtc_get_cfg(&cfg);
//    cfg.nwt_cfg = 1;
//    cfg.tz_cfg = 0;
//    ql_rtc_set_cfg(&cfg);
//    // 
//    ql_rtc_time_t local;
//    do {
//        // 断网重新拨号
//        ql_stop_data_call(ZQ_SIM_0, ZQ_PROFILE_IDX);
//        zq_StartDataCall();
//        // 取utc时间
//        ql_rtc_get_time(&local);
//        if (local.tm_year >= 2023) break;
//        ql_rtos_task_sleep_s(1);
//
//    } while (local.tm_year < 2023)
//}

ZyQueue* ZyCreateMsgQueue(int max_cnt)
{
    if (max_cnt <= 0) return NULL;
    ZyQueue* pQueue = malloc(sizeof(ZyQueue) + max_cnt * sizeof(size_t));
    if (pQueue == NULL) return NULL;
    pQueue->max_cnt = max_cnt;
    pQueue->cnt = 0;
    pQueue->pos = 0;
    ql_rtos_mutex_create(&pQueue->self_mutex);
    return pQueue;
}
// 如果返回不是null, 表示溢出, 返回的是覆盖的那条记录, 可以供caller释放这条记录内存.
void* ZyPostMsg(ZyQueue* pQueue, void* msg)
{

    void* ret = NULL;
    ql_rtos_mutex_lock(pQueue->self_mutex, 100);
    if (pQueue->cnt == pQueue->max_cnt) {  // 满了，取走第一条
        ret = pQueue->buffs[pQueue->pos];
        if (++pQueue->pos == pQueue->max_cnt) pQueue->pos = 0;
        --pQueue->cnt;
    }
    int new_pos = pQueue->pos + pQueue->cnt;
    if (new_pos >= pQueue->max_cnt) new_pos = new_pos - pQueue->max_cnt;
    pQueue->buffs[new_pos] = msg;
    ++pQueue->cnt;
    ql_rtos_mutex_unlock(pQueue->self_mutex);
    return ret;
}
void* zq_GetMsg(ZyQueue* pQueue)
{
    ql_rtos_mutex_lock(pQueue->self_mutex, 100);
    if (pQueue->cnt <= 0) return NULL;
    void* ret = pQueue->buffs[pQueue->pos];
    if (++pQueue->pos == pQueue->max_cnt) pQueue->pos = 0;
    --pQueue->cnt;
    ql_rtos_mutex_unlock(pQueue->self_mutex);
    return ret;
}
int zq_QueueGetCount(ZyQueue* pQueue)
{
    return pQueue->cnt;
}



typedef struct {
	int max_cnt;		// 最大记录条数
	// int rec_size;		// 记录的最大size
	int cnt;			// 实际有多少条记录
	int pos;			// 指向下一个新记录存放位置
	void *bufs[1];		// Circular Stack 记录数组地址
}zq_CircularStack;

// int zq_StackInit(zq_CircularStack *pStack, int Count, int RecSize)
int zq_StackCreate(zq_StackHandle *psh, int max_cnt)
{
	if (max_cnt <= 0) return -1;
	zq_CircularStack * pStack = malloc(sizeof(zq_CircularStack) + (max_cnt - 1) * sizeof(size_t));
	if(pStack == NULL) return -1;
	pStack->max_cnt = max_cnt;
	// pStack->rec_size = RecSize;
	// for(int i = 0; i < max_cnt; i++) {
	// 	(pStack->bufs[i]) = malloc(RecSize);
	// 	if(pStack->bufs[i] == NULL){
	// 		for(int j = 0; i < i; j++)
	// 			free(pStack->bufs[j]);
	// 		return -1;
	// 	}
	// }
	pStack->cnt = 0;
	pStack->pos = 0;
	*psh = (zq_StackHandle)pStack;
	return 0;
}
// 如果返回不是null, 表示溢出, 返回的是覆盖的那条记录, 可以供caller释放这条记录内存.
void *zq_StackPush(zq_StackHandle psh, void *pRec)
{
	zq_CircularStack *pStack = (zq_CircularStack *)psh;
	void *ret = pStack->bufs[pStack->pos];
	pStack->bufs[pStack->pos] = pRec;
	if((++pStack->pos) >= pStack->max_cnt) 
		pStack->pos = 0;
	if((++pStack->cnt) > pStack->max_cnt){
		pStack->cnt = pStack->max_cnt;
		return ret;
	} 
	return NULL;
}
void *zq_StackPop(zq_StackHandle psh)
{	
	zq_CircularStack *pStack = (zq_CircularStack *)psh;
	if(pStack->cnt <= 0)
		return NULL;
	if((--pStack->pos) < 0) 
		pStack->pos = pStack->max_cnt - 1;
	--pStack->cnt;
	// if((--pStack->cnt) < 0) 	// 不存在这个case
	// 	pStack->cnt = 0;
	return pStack->bufs[pStack->pos];
}
int zq_StackGetCount(zq_StackHandle *psh)
{
	zq_CircularStack *pStack = (zq_CircularStack *)psh;
	return pStack->cnt;
}	

// return the time span(ms) between the two continous calling this function.	
//int zq_GetTimeSpan(void)
//{
//    static long long last_ms = 0;
//    long long new_ms = ql_rtos_up_time_ms();
//    int ret = (int)(new_ms - last_ms);
//    last_ms = new_ms;
//    return ret;
//}

// int zq_StackInit(zq_CircularStack *pStack, int Count, int RecSize)
ZqCircularFifo* ZqFifoCreate(const char* path, int max_cnt)
{
    int n = strlen(path);
    if (n > 120) return NULL;  // 完整路径名为path+/+pkg_xx, len < 128
    zq_EasyMkDir(path);

    ZqCircularFifo* pf = malloc(sizeof(ZqCircularFifo) + n + 1);
    if (pf == NULL) return NULL;

    char file_name[128 + 1];
    sprintf(file_name, "%s/pkg_idx", path); // 拼写文件名
    if (zq_EasyFileRead(file_name, pf, sizeof(ZqCircularFifo)) != sizeof(ZqCircularFifo)) { // not record, create
        pf->max_cnt = max_cnt;
        pf->cnt = 0;
        pf->pos = 0;
        strcpy(pf->dir, path);
        zq_EasyFileWrite(file_name, pf, sizeof(ZqCircularFifo));
    }
    else {
        pf->max_cnt = max_cnt;
        strcpy(pf->dir, path);
    }

    return pf;
}

// return msg positon number
int ZqFifoAdd(ZqCircularFifo* pf, const char* msg)
{
    int ret;

    if (pf->pos >= (pf->max_cnt + ZQ_OFFLINE_PKG_SIZE)) pf->pos = 0; // overflow, return to 0
    int cur_pos = pf->pos;

    char file_name[128 + 1];
    int pkg_idx = pf->pos / ZQ_OFFLINE_PKG_SIZE;
    sprintf(file_name, "%s/pkg_%d", pf->dir, pkg_idx); // 拼写文件名
    if (pf->pos % ZQ_OFFLINE_PKG_SIZE == 0) // switch to next new pkg
        ret = ql_remove(file_name);

    QFILE fp = ql_fopen(file_name, "a");
    if (fp <= 0) return ZQ_RET_ERR;
    ret = ql_fputs(msg, fp);
    ret = ql_fputs("\n", fp);
    ql_fclose(fp);
    if (ret <= 0) return ZQ_RET_ERR;

    if ((++pf->cnt) > pf->max_cnt) pf->cnt = pf->max_cnt;
    if ((++pf->pos) > (pf->max_cnt + ZQ_OFFLINE_PKG_SIZE)) pf->pos = 0;

    sprintf(file_name, "%s/pkg_idx", pf->dir); // 拼写文件名
    ret = zq_EasyFileWrite(file_name, pf, sizeof(ZqCircularFifo));
    if(ret <= 0) return ret;
    return cur_pos;
}

// return msg cnt
int ZqFifoGetMsgCnt(ZqCircularFifo* pf)
{
    return pf->cnt;
}
// return true msg cnt
int ZqFifoGet(ZqCircularFifo* pf, char* msg_buf, int msg_buf_size, int msg_cnt)
{
    int step = 0;
    char file_name[128 + 1];
    char buf[ZQ_OFFLINE_MSG_SIZE];
    QFILE fp = 0;

    if (pf->cnt == 0 || msg_cnt <= 0) return 0;

    int start = pf->pos - pf->cnt;
    if (start < 0) start += (pf->max_cnt + ZQ_OFFLINE_PKG_SIZE);

    if (msg_cnt > pf->cnt) msg_cnt = pf->cnt;
    int i = 0;
    for (; i < msg_cnt; i++) {
        int pkg_idx = (start + i) / ZQ_OFFLINE_PKG_SIZE;
        int msg_idx = (start + i) % ZQ_OFFLINE_PKG_SIZE;

        if (i == 0) {
            sprintf(file_name, "%s/pkg_%d", pf->dir, pkg_idx); // 拼写文件名
            fp = ql_fopen(file_name, "r");
            if (fp <= 0) return ZQ_RET_ERR;
            for (int j = 0; j < msg_idx; j++)
                ql_fgets(buf, sizeof(buf), fp);
        }
        else if (msg_idx == 0) { // pkg is over, switch to next pkg, reopen new pkg file
            ql_fclose(fp);
            if (pkg_idx >= (pf->max_cnt / ZQ_OFFLINE_PKG_SIZE + 1)) pkg_idx = 0; // pkg_idx overflow
            sprintf(file_name, "%s/pkg_%d", pf->dir, pkg_idx); // 拼写文件名
            fp = ql_fopen(file_name, "r");
            if (fp <= 0) return ZQ_RET_ERR;
        }

        if (ql_fgets(buf, sizeof(buf), fp) == NULL) break;
        int len = strlen(buf) - 1; //  remove the "\n"
        if (len < 0) break;

        if ((step + len) > (msg_buf_size - 1)) return i; // left size is not enough 
        memcpy(msg_buf + step, buf, len);
        step += len;
    }
    if (fp) ql_fclose(fp);
    *(msg_buf + step) = 0;
    return i;
}

int ZqFifoRemove(ZqCircularFifo* pf, int msg_cnt)
{
    char file_name[128 + 1];
    int ret = pf->cnt;
    if (pf->cnt <= 0) return pf->cnt;

    pf->cnt -= msg_cnt;
    if (pf->cnt < 0) pf->cnt = 0;
    else ret = msg_cnt;
    sprintf(file_name, "%s/pkg_idx", pf->dir); // 拼写文件名
    zq_EasyFileWrite(file_name, pf, sizeof(ZqCircularFifo));
    return ret;
}
